// Copyright 2008 Henrik Rydgard (hrydgard@gmail.com).

#include <math.h>

#include "synoscillator.h"

namespace Synth
{

void Oscillator::SetFreq(float note) {
	if (note < 0)
		note = 0;
	if (note > 127)
		note = 127;
	note += note_offset;
	if (type == ot_DLS) {
		float scale = pow(2.0f, note / 12.0f) * sr;
		delta = (unsigned int)((float)0x1000000 * scale);
	} else {
		delta = pow(2.0f, (float)(note - 69.0f) / 12.0f) * 440.f * ((float)0x100000000ULL / (float)INTERNAL_SAMPLE_RATE);
	}
}

int Oscillator::Start(int type, int ins, float note) {
	char hej = ins & 0xFF;
	if (ins < 0)
		ins = (int)(unsigned char)hej;

	pos = 0; // bad idea
	v = 0;
	delta = 0;
	sample = 0;
	samplefrac = 0;
	note_offset = 0;
	this->type = type;
	if (type == ot_DLS) {
		int reg = dls.GetRegionAndNoteOffset(ins, note, &note_offset, &sr);
		if (reg != -1) {
			Audio::DLS::Instrument *instrument = &dls.instruments[ins];
			Audio::DLS::Region *region = &instrument->regions[reg];
			int group = region->header->keyGroup;
			this->region = region;
			playing = true;
			return group;
		}
	} else {
		playing = true;
	}
	return 0;
}

void Oscillator::Render(Block buffer, const float *fm_input) {
	Clear(buffer);
	if (!playing)
		return;
	if (type == ot_DLS) {
		const Audio::DLS::RegionLoop *loop = region->loop;
		const short *srcData = dls.samples[region->waveIndex].pointer; 
		const int sample_length = dls.samples[region->waveIndex].length;
		FORBLOCK(i) {
			if (loop) {
				if (sample >= loop->loopStart + loop->loopLength)
					sample -= loop->loopLength;
			} else {
				if (sample >= sample_length) {
					playing = false;
					break;
				}
			}
			float s1 = (float)srcData[sample] * (1.0f / 32768.0f);
			int next_sample = sample + 1;
			if (loop && next_sample >= loop->loopStart + loop->loopLength)
				next_sample -= loop->loopLength;
			else if (next_sample >= sample_length)
				next_sample = sample;
			float s2 = (float)srcData[next_sample] * (1.0f / 32768.0f);
			float frc = (float)samplefrac / (float)0x1000000;

			//buffer[i] += (1-frc)*s1+frc*s2; //lerp
			float s = ((1 - frc)*s1 + frc*s2); //ThirdInterp(frc, s0,s1,s2,s3);
			buffer[i] = 2 * s; // GM sounds are weak 
			samplefrac += delta;
			sample += samplefrac >> 24;
			samplefrac &= 0xFFFFFF;
		}
		return;
	}
	//Okay, grab modulations
	// TODO: fix this equation
	float hardSync = 1.0f + 0.03*(int)(unsigned char)(char)(127*parameterTable[paramOffset + cp_Osc1InstrHardSync]);
	if (hardSync < 1.0f)
		hardSync = 1.0f;
	//Oh, an analog osc
	float pw = parameterTable[paramOffset + cp_Osc1PulseWidth];
	double fs = delta / (double)0x100000000ULL;
	FORBLOCK(i) {
		float prev = fm_input ? fm_input[i] : 0;
		float fm = prev * parameterTable[paramOffset + cp_Osc1FM];
		float PW = parameterTable[paramOffset + cp_Osc1PulseWidth];
		// create waveform
		pos += fs;
		float value;
		if (type == ot_Pulse || type == ot_Saw) {
			// add BLEP at end of waveform
			if (pos >= 1) {
				pos -= 1.0;
				v = 0.0f;
				blep.AddBlep(pos / fs, 1.0);
			}

			// add BLEP in middle of waveform for pulse/squarewave
			if (!v && pos > PW && type == ot_Pulse)
			{
				v = 1.0;
				blep.AddBlep((pos - PW) / fs, -1.0);
			}

			double out_v;
			// sample value
			if (type == ot_Saw)
				out_v = pos;
			else
				out_v = v;

			// TODO: remove pulse wave DC offset
			// add BLEP buffer contents
			value = 2 * (out_v + blep.Advance()) - 1;
		} else if (type == ot_WhiteNoise) {
			value = GenerateRandomSignedFloat();
		} else if (type == ot_Sine) {
			value = sinf((pos + fm) * 2 * PI);
		} else if (type == ot_Triangle) {
			value = asin(sinf((pos + fm)*2*PI));
		}

		if (parameterTableInt[paramOffset + cp_Osc1RingMod])
			value *= prev;

		buffer[i] = value;
	}
}
}